1   /*
2    * Copyright (C) 2010 The Guava Authors
3    *
4    * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
5    * in compliance with the License. You may obtain a copy of the License at
6    *
7    * http://www.apache.org/licenses/LICENSE-2.0
8    *
9    * Unless required by applicable law or agreed to in writing, software distributed under the License
10   * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
11   * or implied. See the License for the specific language governing permissions and limitations under
12   * the License.
13   */
14  
15  package com.google.common.collect;
16  
17  import static com.google.common.base.Preconditions.checkNotNull;
18  
19  import com.google.common.annotations.Beta;
20  import com.google.common.annotations.GwtCompatible;
21  import com.google.common.base.Function;
22  
23  import java.util.Collections;
24  import java.util.Comparator;
25  import java.util.List;
26  import java.util.RandomAccess;
27  
28  import javax.annotation.Nullable;
29  
30  /**
31   * Static methods pertaining to sorted {@link List} instances.
32   *
33   * In this documentation, the terms <i>greatest</i>, <i>greater</i>, <i>least</i>, and
34   * <i>lesser</i> are considered to refer to the comparator on the elements, and the terms
35   * <i>first</i> and <i>last</i> are considered to refer to the elements' ordering in a
36   * list.
37   *
38   * @author Louis Wasserman
39   */
40  @GwtCompatible
41  @Beta final class SortedLists {
42    private SortedLists() {}
43  
44    /**
45     * A specification for which index to return if the list contains at least one element that
46     * compares as equal to the key.
47     */
48    public enum KeyPresentBehavior {
49      /**
50       * Return the index of any list element that compares as equal to the key. No guarantees are
51       * made as to which index is returned, if more than one element compares as equal to the key.
52       */
53      ANY_PRESENT {
54        @Override
55        <E> int resultIndex(
56            Comparator<? super E> comparator, E key, List<? extends E> list, int foundIndex) {
57          return foundIndex;
58        }
59      },
60      /**
61       * Return the index of the last list element that compares as equal to the key.
62       */
63      LAST_PRESENT {
64        @Override
65        <E> int resultIndex(
66            Comparator<? super E> comparator, E key, List<? extends E> list, int foundIndex) {
67          // Of course, we have to use binary search to find the precise
68          // breakpoint...
69          int lower = foundIndex;
70          int upper = list.size() - 1;
71          // Everything between lower and upper inclusive compares at >= 0.
72          while (lower < upper) {
73            int middle = (lower + upper + 1) >>> 1;
74            int c = comparator.compare(list.get(middle), key);
75            if (c > 0) {
76              upper = middle - 1;
77            } else { // c == 0
78              lower = middle;
79            }
80          }
81          return lower;
82        }
83      },
84      /**
85       * Return the index of the first list element that compares as equal to the key.
86       */
87      FIRST_PRESENT {
88        @Override
89        <E> int resultIndex(
90            Comparator<? super E> comparator, E key, List<? extends E> list, int foundIndex) {
91          // Of course, we have to use binary search to find the precise
92          // breakpoint...
93          int lower = 0;
94          int upper = foundIndex;
95          // Of course, we have to use binary search to find the precise breakpoint...
96          // Everything between lower and upper inclusive compares at <= 0.
97          while (lower < upper) {
98            int middle = (lower + upper) >>> 1;
99            int c = comparator.compare(list.get(middle), key);
100           if (c < 0) {
101             lower = middle + 1;
102           } else { // c == 0
103             upper = middle;
104           }
105         }
106         return lower;
107       }
108     },
109     /**
110      * Return the index of the first list element that compares as greater than the key, or {@code
111      * list.size()} if there is no such element.
112      */
113     FIRST_AFTER {
114       @Override
115       public <E> int resultIndex(
116           Comparator<? super E> comparator, E key, List<? extends E> list, int foundIndex) {
117         return LAST_PRESENT.resultIndex(comparator, key, list, foundIndex) + 1;
118       }
119     },
120     /**
121      * Return the index of the last list element that compares as less than the key, or {@code -1}
122      * if there is no such element.
123      */
124     LAST_BEFORE {
125       @Override
126       public <E> int resultIndex(
127           Comparator<? super E> comparator, E key, List<? extends E> list, int foundIndex) {
128         return FIRST_PRESENT.resultIndex(comparator, key, list, foundIndex) - 1;
129       }
130     };
131     abstract <E> int resultIndex(
132         Comparator<? super E> comparator, E key, List<? extends E> list, int foundIndex);
133   }
134 
135   /**
136    * A specification for which index to return if the list contains no elements that compare as
137    * equal to the key.
138    */
139   public enum KeyAbsentBehavior {
140     /**
141      * Return the index of the next lower element in the list, or {@code -1} if there is no such
142      * element.
143      */
144     NEXT_LOWER {
145       @Override
146       int resultIndex(int higherIndex) {
147         return higherIndex - 1;
148       }
149     },
150     /**
151      * Return the index of the next higher element in the list, or {@code list.size()} if there is
152      * no such element.
153      */
154     NEXT_HIGHER {
155       @Override
156       public int resultIndex(int higherIndex) {
157         return higherIndex;
158       }
159     },
160     /**
161      * Return {@code ~insertionIndex}, where {@code insertionIndex} is defined as the point at
162      * which the key would be inserted into the list: the index of the next higher element in the
163      * list, or {@code list.size()} if there is no such element.
164      *
165      * <p>Note that the return value will be {@code >= 0} if and only if there is an element of the
166      * list that compares as equal to the key.
167      *
168      * <p>This is equivalent to the behavior of
169      * {@link java.util.Collections#binarySearch(List, Object)} when the key isn't present, since
170      * {@code ~insertionIndex} is equal to {@code -1 - insertionIndex}.
171      */
172     INVERTED_INSERTION_INDEX {
173       @Override
174       public int resultIndex(int higherIndex) {
175         return ~higherIndex;
176       }
177     };
178 
179     abstract int resultIndex(int higherIndex);
180   }
181 
182   /**
183    * Searches the specified naturally ordered list for the specified object using the binary search
184    * algorithm.
185    *
186    * <p>Equivalent to {@link #binarySearch(List, Function, Object, Comparator, KeyPresentBehavior,
187    * KeyAbsentBehavior)} using {@link Ordering#natural}.
188    */
189   public static <E extends Comparable> int binarySearch(List<? extends E> list, E e,
190       KeyPresentBehavior presentBehavior, KeyAbsentBehavior absentBehavior) {
191     checkNotNull(e);
192     return binarySearch(
193         list, checkNotNull(e), Ordering.natural(), presentBehavior, absentBehavior);
194   }
195 
196   /**
197    * Binary searches the list for the specified key, using the specified key function.
198    *
199    * <p>Equivalent to {@link #binarySearch(List, Function, Object, Comparator, KeyPresentBehavior,
200    * KeyAbsentBehavior)} using {@link Ordering#natural}.
201    */
202   public static <E, K extends Comparable> int binarySearch(List<E> list,
203       Function<? super E, K> keyFunction, @Nullable K key, KeyPresentBehavior presentBehavior,
204       KeyAbsentBehavior absentBehavior) {
205     return binarySearch(
206         list,
207         keyFunction,
208         key,
209         Ordering.natural(),
210         presentBehavior,
211         absentBehavior);
212   }
213 
214   /**
215    * Binary searches the list for the specified key, using the specified key function.
216    *
217    * <p>Equivalent to
218    * {@link #binarySearch(List, Object, Comparator, KeyPresentBehavior, KeyAbsentBehavior)} using
219    * {@link Lists#transform(List, Function) Lists.transform(list, keyFunction)}.
220    */
221   public static <E, K> int binarySearch(
222       List<E> list,
223       Function<? super E, K> keyFunction,
224       @Nullable K key,
225       Comparator<? super K> keyComparator,
226       KeyPresentBehavior presentBehavior,
227       KeyAbsentBehavior absentBehavior) {
228     return binarySearch(
229         Lists.transform(list, keyFunction), key, keyComparator, presentBehavior, absentBehavior);
230   }
231 
232   /**
233    * Searches the specified list for the specified object using the binary search algorithm. The
234    * list must be sorted into ascending order according to the specified comparator (as by the
235    * {@link Collections#sort(List, Comparator) Collections.sort(List, Comparator)} method), prior
236    * to making this call. If it is not sorted, the results are undefined.
237    *
238    * <p>If there are elements in the list which compare as equal to the key, the choice of
239    * {@link KeyPresentBehavior} decides which index is returned. If no elements compare as equal to
240    * the key, the choice of {@link KeyAbsentBehavior} decides which index is returned.
241    *
242    * <p>This method runs in log(n) time on random-access lists, which offer near-constant-time
243    * access to each list element.
244    *
245    * @param list the list to be searched.
246    * @param key the value to be searched for.
247    * @param comparator the comparator by which the list is ordered.
248    * @param presentBehavior the specification for what to do if at least one element of the list
249    *        compares as equal to the key.
250    * @param absentBehavior the specification for what to do if no elements of the list compare as
251    *        equal to the key.
252    * @return the index determined by the {@code KeyPresentBehavior}, if the key is in the list;
253    *         otherwise the index determined by the {@code KeyAbsentBehavior}.
254    */
255   public static <E> int binarySearch(List<? extends E> list, @Nullable E key,
256       Comparator<? super E> comparator, KeyPresentBehavior presentBehavior,
257       KeyAbsentBehavior absentBehavior) {
258     checkNotNull(comparator);
259     checkNotNull(list);
260     checkNotNull(presentBehavior);
261     checkNotNull(absentBehavior);
262     if (!(list instanceof RandomAccess)) {
263       list = Lists.newArrayList(list);
264     }
265     // TODO(user): benchmark when it's best to do a linear search
266 
267     int lower = 0;
268     int upper = list.size() - 1;
269 
270     while (lower <= upper) {
271       int middle = (lower + upper) >>> 1;
272       int c = comparator.compare(key, list.get(middle));
273       if (c < 0) {
274         upper = middle - 1;
275       } else if (c > 0) {
276         lower = middle + 1;
277       } else {
278         return lower + presentBehavior.resultIndex(
279             comparator, key, list.subList(lower, upper + 1), middle - lower);
280       }
281     }
282     return absentBehavior.resultIndex(lower);
283   }
284 }